package org.yeasy.redundance.util;


import org.yeasy.common.util.CommonBeanUtil;
import org.yeasy.redundance.bean.MyEntry;

import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * 批量冗余更新代理.
 * The type Redundance proxy.
 *
 * @param <T> the type parameter
 */
public class BatchRedundanceProxy<T> implements FillRedundanceInterface<T>{
    private Collection<T> target;

    /**
     * Instantiates a new Redundance proxy.
     *
     * @param target the target
     */
    public BatchRedundanceProxy(Collection<T> target) {
        this.target = target;
    }

    @Override
    public FillRedundanceInterface<T> fillRedundanceFromValue(Object value, List<String> redundanceKeyList) {
        for (T targetEach : this.target) {
            for (String redundanceKey : redundanceKeyList) {
                CommonBeanUtil.invokeSetter(targetEach,redundanceKey,value);
            }
        }

        return this;
    }

    @Override
    public FillRedundanceInterface<T> fillRedundanceFromSingle(Object info, List<String> redundanceKeyList) {
        Map<String, String> mapTargetKey2SourceKey=new HashMap<>();
        for (String redundanceKey : redundanceKeyList) {
            String[] rddKeys = redundanceKey.split(" ");
            if (rddKeys.length==2) {
                mapTargetKey2SourceKey.put(rddKeys[1],rddKeys[0]);
            }else{
                mapTargetKey2SourceKey.put(redundanceKey,redundanceKey);
            }
        }
        return fillRedundanceFromSingle(info,mapTargetKey2SourceKey);
    }

    @Override
    public FillRedundanceInterface<T> fillRedundanceFromSingle(Object info, Map<String, String> targetKey2InfoKeyMap) {
        if(info!=null){
            for (T targetEach : this.target) {
                for (Map.Entry<String, String> targetKey2InfoKey : targetKey2InfoKeyMap.entrySet()) {
                    Object infoVal = CommonBeanUtil.invokeGetter(info, targetKey2InfoKey.getValue());
                    CommonBeanUtil.invokeSetter(targetEach,targetKey2InfoKey.getKey(),infoVal);
                }
            }

        }
        return this;
    }

    /**
     * Fill redundant.
     *
     * @param infoList          the info list
     * @param key               the key
     * @param redundanceKeyList the redundance key list
     */
    @Override
    public FillRedundanceInterface<T> fillRedundanceFromList(List<?> infoList, String key, List<String> redundanceKeyList){
        Map.Entry<String, String> keySourceEntry = new MyEntry<>(key, key);
        return fillRedundanceFromList(infoList,keySourceEntry,redundanceKeyList);
    }

    @Override
    public FillRedundanceInterface<T> fillRedundanceFromList(List<?> infoList, Map.Entry<String, String> keySource, List<String> redundanceKeyList) {
        Map<String, String> mapTargetKey2InfoKey=new HashMap<>();
        for (String redundanceKey : redundanceKeyList) {
            String[] rddKeys = redundanceKey.split(" ");
            if (rddKeys.length==2) {
                mapTargetKey2InfoKey.put(rddKeys[1],rddKeys[0]);
            }else{
                mapTargetKey2InfoKey.put(redundanceKey,redundanceKey);
            }
        }
        return fillRedundanceFromList(infoList,keySource,mapTargetKey2InfoKey);
    }

    /**
     * Fill redundant.
     *
     * @param infoList             the info list
     * @param targetKey2InfoKeyMap the info key 2 target key map
     */
    @Override
    public FillRedundanceInterface<T> fillRedundanceFromList(List<?> infoList, Map.Entry<String,String> keySource, Map<String,String> targetKey2InfoKeyMap){
        String[] sourceKeys = keySource.getKey().split(" ");
        String[] sourceValues=keySource.getValue().split(" ");
        BiFunction<Object,String[], String> calcInfo2Key = (u, ks) -> Arrays.stream(ks)
                .map(k-> {
                    Object v=CommonBeanUtil.invokeGetter(u,k);
                    return v!=null?v.toString():null;
                })
                .reduce("", (s, s2) -> {
                    if(s==null||s2==null){
                        return null;
                    }
                    return (s+" "+s2).trim();
                });

        Map<String, ?> mapInfoId2Entity = infoList.stream().collect(Collectors.toMap(item->calcInfo2Key.apply(item,sourceKeys), item -> item));
        for (T targetEach : this.target) {
            String unitKey = calcInfo2Key.apply(targetEach, sourceValues);
            if(unitKey==null){
                continue;
            }
            Object info=mapInfoId2Entity.get(unitKey);
            if(info!=null){
                for (Map.Entry<String, String> infoKey2TargetKey : targetKey2InfoKeyMap.entrySet()) {
                    Object infoVal = CommonBeanUtil.invokeGetter(info, infoKey2TargetKey.getValue());
                    CommonBeanUtil.invokeSetter(targetEach,infoKey2TargetKey.getKey(),infoVal);
                }
            }
        }
        return this;

    }
}
